﻿'    This file is part of The Global Distribution System.

'    The Global Distribution System is free software: you can redistribute it and/or modify
'    it under the terms of the GNU General Public License as published by
'    the Free Software Foundation, either version 3 of the License, or
'    (at your option) any later version.

'    The Global Distribution System is distributed in the hope that it will be useful,
'    but WITHOUT ANY WARRANTY; without even the implied warranty of
'    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'    GNU General Public License for more details.

'    You should have received a copy of the GNU General Public License
'    along with The Global Distribution System.  If not, see <http://www.gnu.org/licenses/>.

Imports System.Net.Sockets
Imports System.Net

Namespace NetCode

    Public Class NetException
        Inherits Exception

        Private _location As String = String.Empty
        Private _errorMessage As String = String.Empty
        Private _stack As String = String.Empty

        ''' <summary>
        ''' Creates a new NetException Class.
        ''' </summary>
        ''' <param name="ErrorMessage">The Error Message.</param>
        ''' <param name="location">The location of the error.</param>
        ''' <remarks></remarks>
        Public Sub New(ByVal ErrorMessage As String, ByVal location As String, ByVal stackTrace As String)
            _errorMessage = ErrorMessage
            _location = location
            _stack = stackTrace
        End Sub

        ''' <summary>
        ''' Returns the Error Message.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property Message As String
            Get
                Return _errorMessage
            End Get
        End Property

        ''' <summary>
        ''' Returns the source of the error.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides Property Source As String
            Get
                Return _location
            End Get
            Set(ByVal value As String)
                'Discard.
            End Set
        End Property

        ''' <summary>
        ''' Returns the StackTrace from the Exception.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property StackTrace As String
            Get

                Return _stack
            End Get
        End Property

    End Class

    Public Class Net

        Private _listener As TcpListener
        Private Property Online As Boolean = False
        Private _listenerThread As New System.Threading.Thread(AddressOf Listener)
        Private _serverList As New List(Of Server)

        ''' <summary>
        ''' Individual Connections to other servers.
        ''' </summary>
        ''' <remarks></remarks>
        Private Class Server
            Implements IDisposable, IEquatable(Of Server)

            Private _ip As String = String.Empty
            Private _disposed As Boolean = False
            Private _connection As TcpClient

            ''' <summary>
            ''' Returns the IP of the server.
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public ReadOnly Property IP As String
                Get
                    If Not IsDisposed Then Return Me._ip Else Throw New ObjectDisposedException(String.Format("Server IP: {0}", _ip))
                End Get
            End Property

            ''' <summary>
            ''' Returns if the server is disposed of or not.
            ''' </summary>
            ''' <value></value>
            ''' <returns></returns>
            ''' <remarks></remarks>
            Public ReadOnly Property IsDisposed As Boolean
                Get
                    Return _disposed
                End Get
            End Property
            
            ''' <summary>
            ''' Creates a new Server Class.
            ''' </summary>
            ''' <param name="client">The TcpClient to handle.</param>
            ''' <remarks></remarks>
            Public Sub New(ByRef client As TcpClient)
                _connection = client
            End Sub

            Private Sub Listener()

            End Sub


            ''' <summary>
            ''' Queries the TcpClient and gets the server information.
            ''' </summary>
            ''' <remarks></remarks>
            Private Sub GetIP()
                If IP = String.Empty AndAlso _connection IsNot Nothing Then
                    Try
                        ' Get the clients IP address using Client property            
                        Dim ipend As IPEndPoint = CType(_connection.Client.RemoteEndPoint, IPEndPoint)
                        If Not ipend Is Nothing Then
                            _ip = ipend.Address.ToString
                        End If
                    Catch ex As System.ObjectDisposedException
                        _ip = String.Empty
                    Catch ex As SocketException
                        _ip = String.Empty
                    End Try
                End If
            End Sub

            ''' <summary>
            ''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            ''' </summary>
            ''' <filterpriority>2</filterpriority>
            Public Sub Dispose() Implements IDisposable.Dispose
                Throw New NotImplementedException()
            End Sub

            ''' <summary>
            ''' Indicates whether the current object is equal to another object of the same type.
            ''' </summary>
            ''' <returns>
            ''' true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
            ''' </returns>
            ''' <param name="other">An object to compare with this object.</param>
            Public Function Equals(ByVal other As Server) As Boolean Implements IEquatable(Of Server).Equals
                Return other.IP = Me.IP
            End Function

        End Class

        ''' <summary>
        ''' Sets up the NetCode Class
        ''' </summary>
        ''' <param name="Port">The Port for the server to listen on.</param>
        ''' <remarks></remarks>
        Public Sub New(Optional ByVal Port As Integer = 665I)
            'Setup the Listener.
            _listener = New TcpListener(System.Net.IPAddress.Any, Port)
        End Sub

        ''' <summary>
        ''' Begins listening for incoming server connections.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub [Start]()
            'Say that we're online.
            Me.Online = True
            'Start the listener thread.
            _listenerThread.Start()
        End Sub

        ''' <summary>
        ''' Endless looping thread.
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub Listener()
            'Loop while we're allowed to.
            Do While Online
                Try
                    'Get a request.
                    Dim IncomingConnection As TcpClient = _listener.AcceptTcpClient
                    'Pass it off to a new Server Class.
                Catch ex As Exception
                    'ToDo: Setup Logging system.
                End Try
            Loop
        End Sub

        ''' <summary>
        ''' Shuts down the NetCode class, terminating the server from the network.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub [Stop]()
            'Set Online to false.
            Online = False
            'Dispose of the listener
            _listener.Server.Close()
            _listener.Stop()
            'Disconnect all connected servers.
            SyncLock _serverList
                For i As Integer = _serverList.Count - 1 To 0 Step -1

                Next
            End SyncLock
        End Sub

        ''' <summary>
        ''' Initiates a connection to another NetCode system.
        ''' </summary>
        ''' <param name="IP">The IP address to connect to.</param>
        ''' <remarks></remarks>
        Public Sub Connect(ByVal IP As String)
            If IsValidIP(IP) Then
                'Begin a connection.

            End If
        End Sub

        ''' <summary>
        ''' Initiates an asynchronous connection to another NetCode system.
        ''' </summary>
        ''' <param name="IP">The IP address to connect to.</param>
        ''' <remarks></remarks>
        Public Sub AsyncConnect(ByVal IP As String)

        End Sub

        ''' <summary>
        ''' Initiates a connection to another NetCode system via Hostname.
        ''' </summary>
        ''' <param name="HostName"></param>
        ''' <remarks></remarks>
        Public Sub ConnectWithHost(ByVal HostName As String)
            Try
                'Get the Host Entry
                Dim HostEntry As IPHostEntry = Dns.GetHostEntry(HostName)
                'Get the first returned IP
                Dim IP As String = HostEntry.AddressList(0).ToString
                'Now call internal Connect() Routine
                Me.Connect(IP)
            Catch ex As Exception
                Throw New NetCode.NetException(ex.Message, String.Format("NetCode.Net.ConnectWithHost(HostName: {0})", HostName), Environment.StackTrace)
            End Try
        End Sub

        ''' <summary>
        ''' method to validate an IP address
        ''' using regular expressions. The pattern
        ''' being used will validate an ip address
        ''' with the range of 1.0.0.0 to 255.255.255.255
        ''' </summary>
        ''' <param name="addr">Address to validate</param>
        ''' <returns></returns>
        Private Function IsValidIP(ByVal addr As String) As Boolean
            'create our match pattern
            Dim pattern As String = "^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\." & _
            "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$"
            'create our Regular Expression object
            Dim check As New Text.RegularExpressions.Regex(pattern)
            'boolean variable to hold the status
            Dim valid As Boolean = False
            'check to make sure an ip address was provided
            If addr = "" Then
                'no address provided so return false
                valid = False
            Else
                'address provided so use the IsMatch Method
                'of the Regular Expression object
                valid = check.IsMatch(addr, 0)
            End If
            'return the results
            Return valid
        End Function

    End Class

End Namespace